package ATM;

import java.util.Random;

public class ATM {		// contains the Factory method that creates objects of transaction classes
	//http://www.tutorialspoint.com/design_pattern/factory_pattern.htm
	
	// represents an automated teller machine (ATM)
	
	private boolean userAuthenticated;
	private int currentAccountNumber;	
	
	private Screen screen;
	private Keypad keypad;
	private CashDispenser cashDispenser;
	private DepositSlot depositSlot;
	private BankDatabase bankDatabase;
	
	private static int accountNumber;
	private static int pin;
	private static Random random;
	
	public ATM()
	{
		userAuthenticated = false;
		currentAccountNumber = 0;
		screen = Screen.getInstance();
		keypad = new Keypad();
		cashDispenser = new CashDispenser();
		depositSlot = new DepositSlot();
		bankDatabase = new BankDatabase();
	}
	
	static  //static block to initialize account number and PIN without having to insert numbers in the command window
			//it uses a Random generator in order to automatically switch between the 2 example accounts when the ATM is started 
		{
			random = new Random();
			
			if (random.nextInt()%2 == 0)
			{	
				accountNumber = 12345;
				pin = 54321;
			}
			else
			{
				accountNumber = 98765;
				pin = 56789;
			}
		}
	
	public void start()  // starts the ATM session
	{
		while(true)
		{
			while(!userAuthenticated) //initially the user is not authenticated
			{
				screen.displayMessageLine("\nWelcome!");
				screen.displayMessageLine("\nYour account number is: ");
				screen.displayAccountNumber(accountNumber);
				authenticateUser();  //checks if the inserted account number and PIN are recognized by the bank database
			}
			
			performTransactions(); //when user authenticated, start running transactions
			userAuthenticated = false;
			currentAccountNumber = 0;
			screen.displayMessageLine("\nThank you! GoodBye!");
		}
	}
		
	private void authenticateUser() //checks if the inserted account number and PIN are recognized by the bank database
	{		
		userAuthenticated = bankDatabase.authenticateUser(accountNumber, pin);
		
		if(userAuthenticated)
		{
			currentAccountNumber = accountNumber;
		}
		else
			screen.displayMessageLine("Invalid account number or PIN. Please try again."); 
	}
	
	private void performTransactions()
	{
		ExecuteTransaction currentTransaction = null;
		boolean userExited = false;
		
		while (!userExited)
		{
			int mainMenuSelection = displayMainMenu();
			
			switch (mainMenuSelection)
			{
				case 1:
				case 2:
				case 3:			
					
					currentTransaction = createTransaction(mainMenuSelection); // a transaction will be created according with the option chosen by the user
					currentTransaction.execute();
					
				break;				
				case 4:
					screen.displayMessageLine("\nExiting the system...");
					userExited = true;
				break;				
				default:
					screen.displayMessageLine("\nYou did not enter a valid selection. Try again.");
				break;				
			}
		}
	}
	
	private int displayMainMenu() // the menu is displayed on the screen with the help of methods provided by the Screen class
	{							//the method return an integer which is the option chosen by the user. This is necessary in the performTransactions() method
		screen.displayMessageLine("\n\nMain menu:");
		screen.displayMessageLine("\n1 - View my balance");
		screen.displayMessageLine("2 - Withdraw cash");
		screen.displayMessageLine("3 - Deposit funds");
		screen.displayMessageLine("4 - Exit\n");
		screen.displayMessageLine("Enter a choice: ");
		return keypad.getInput();
	}
	
	private ExecuteTransaction createTransaction(int type) //the Factory that generates transaction objects
	{													//works with the ExecuteTransaction interface
		ExecuteTransaction temp = null;
		
		switch(type)
		{
			case 1:
				temp = new BalanceInquiry(currentAccountNumber, screen, bankDatabase);
			break;
			case 2:
				temp = new Withdrawal(currentAccountNumber, screen, bankDatabase, keypad, cashDispenser);
			break;
			case 3:
				temp = new Deposit(currentAccountNumber, screen, bankDatabase, keypad, depositSlot);
			break;
		}
		
		return temp;
	}
}